package cn.silver.framework.mybatis.service.impl;

import cn.hutool.json.JSONObject;
import cn.silver.framework.core.exception.ServiceException;
import cn.silver.framework.mybatis.annotation.Table;
import cn.silver.framework.mybatis.annotation.TableField;
import cn.silver.framework.mybatis.bean.BusBean;
import cn.silver.framework.mybatis.bean.PageModel;
import cn.silver.framework.mybatis.domain.BusEntity;
import cn.silver.framework.mybatis.mapper.BusMapperPlus;
import cn.silver.framework.mybatis.param.BusParam;
import cn.silver.framework.mybatis.service.IBusService;
import cn.silver.framework.mybatis.utils.SqlUtils;
import cn.silver.framework.openapi.constant.ResultEnum;
import cn.silver.framework.openapi.model.ResultPage;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

@Slf4j
public abstract class BusServiceImpl<M extends BusMapperPlus<T, V>, T extends BusEntity, P extends BusParam, V extends BusBean>
        extends BaseServiceImpl<M, T, P, V> implements IBusService<T, P, V> {

    @Resource
    protected JdbcTemplate jdbcTemplate;

    @Override
    public ResultPage<V> selectRecycle(PageModel page, P param) {
        ResultPage<V> result = new ResultPage<>();
        Class<T> clazz = this.baseMapper.currentModelClass();
        String tableName = clazz.isAnnotationPresent(Table.class) ? clazz.getAnnotation(Table.class).name() : SqlUtils.getJdbcName(clazz.getSimpleName());
        StringBuilder sql = new StringBuilder(" from " + tableName + " where del_flag = 1");
        if (StringUtils.isNotBlank(param.getCode())) {
            sql.append(" and code like '%").append(param.getCode()).append("%'");
        }
        if (StringUtils.isNotBlank(param.getName())) {
            sql.append(" and name like '%").append(param.getName()).append("%'");
        }
        if (StringUtils.isNotBlank(param.getStatus())) {
            sql.append(" and status = '").append(param.getStatus()).append("'");
        }
        JdbcTemplate jdbcTemplate = this.jdbcTemplate;
        if (jdbcTemplate != null) {
            result.setTotal(jdbcTemplate.queryForObject("select count(*) " + sql, Long.class));
            if (result.getTotal() > 0) {
                List<Map<String, Object>> rows = jdbcTemplate.queryForList("select * " + sql);
                result.setRows(rows.stream().map(item -> new JSONObject(item).toBean(this.currentVoClass())).toList());
            }
        }
        return result;
    }

    protected Long selectExist(V model) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        queryWrapper.ne(StringUtils.isNotBlank(model.getId()), "id", model.getId());
        queryWrapper.and(wrapper -> {
            wrapper.eq(StringUtils.isNotBlank(model.getCode()), "code", model.getCode());
            wrapper.or().eq(StringUtils.isNotBlank(model.getName()), "name", model.getName());
            List<Field> fields = Arrays.stream(model.getClass().getDeclaredFields()).filter(item -> item.isAnnotationPresent(TableField.class)).toList();
            for (Field field : fields) {
                TableField tableField = field.getAnnotation(TableField.class);
                if (tableField.unique()) {
                    wrapper.or().eq(StringUtils.isNotBlank(field.getName()), tableField.name(), model.getId());
                }
            }
        });
        return this.baseMapper.selectCount(queryWrapper);
    }

    public void checkUnique(V model) {
        long exist = this.selectExist(model);
        if (exist > 0) {
            throw new ServiceException(ResultEnum.DATA_ERROR_EXIST.getCode(), "数据保存失败，存在重复记录");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public boolean insert(V model) {
        this.checkUnique(model);
        return super.insert(model);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public boolean update(V model) {
        this.checkUnique(model);
        return super.update(model);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public int remove(String id) {
        Class<T> clazz = this.baseMapper.currentModelClass();
        String tableName = clazz.isAnnotationPresent(Table.class) ? clazz.getAnnotation(Table.class).name() : SqlUtils.getJdbcName(clazz.getSimpleName());
        return jdbcTemplate.update("delete from " + tableName + " where id = ?", id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public int removeBatch(Collection<String> ids) {
        Class<T> clazz = this.baseMapper.currentModelClass();
        String tableName = clazz.isAnnotationPresent(Table.class) ? clazz.getAnnotation(Table.class).name() : SqlUtils.getJdbcName(clazz.getSimpleName());
        return jdbcTemplate.update("delete from " + tableName + " where id in (?)", ids.toArray());
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public int restore(String id) {
        Class<T> clazz = this.baseMapper.currentModelClass();
        String tableName = clazz.isAnnotationPresent(Table.class) ? clazz.getAnnotation(Table.class).name() : SqlUtils.getJdbcName(clazz.getSimpleName());
        return jdbcTemplate.update("update " + tableName + " set del_flag = 0 where id = ?", id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public int restoreBatch(Collection<String> ids) {
        Class<T> clazz = this.baseMapper.currentModelClass();
        String tableName = clazz.isAnnotationPresent(Table.class) ? clazz.getAnnotation(Table.class).name() : SqlUtils.getJdbcName(clazz.getSimpleName());
        return jdbcTemplate.update("update " + tableName + " set del_flag = 0 where id in (?)", ids.toArray());
    }

    @Override
    public QueryWrapper<T> buildQueryWrapper(P params) {
        QueryWrapper<T> wrapper = super.buildQueryWrapper(params);
        wrapper.like(StringUtils.isNotBlank(params.getCode()), "code", params.getCode());
        wrapper.like(StringUtils.isNotBlank(params.getName()), "name", params.getName());
        wrapper.eq(StringUtils.isNotBlank(params.getStatus()), "status", params.getStatus());
        wrapper.orderByAsc("sort");
        return wrapper;
    }
}
